package auth

import (
	"context"
	"errors"
	"fmt"
	"pd/server/com"
	"pd/server/com/errno"
	table "pd/server/data/user"
	"pd/server/plugin/api"
	"pd/server/plugin/logger"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

const (
	CustomClaimsKey  = "___CustomClaimsKey"
	CustomTokenKey   = "___CustomTokenKey"
	CustomConfigKey  = "___CustomConfigKey"
	RpcTokenKey      = "___rpc_token"
	RpcTokenPassword = "k3LJA32lsik"

	TxCloudApiSecretId  = ""
	TxCloudApiSecretKey = ""

	AdminIdKey = 1000

	// 系统ID
	SysIdAuth       = 1 // 权限管理系统
	SysIdDdy        = 2 // 豆豆云
	SysIdCompanySys = 5 // 品牌商系统

	// 系统内置角色ID
	RoleIdDdySysManager = 11 // 豆豆云-系统管理员
	RoleIdDdyCompany    = 12 // 豆豆云-品牌商
	RoleIdDdyAgent      = 13 // 豆豆云-代理商
	RoleIdDevice        = 14 // 设备管理员
	RoleIdSharing       = 20 // 分账人

	RoleIdCompanyManager = 110 // 品牌商系统-管理员

	TokenExpiredSeconds = 86400 * 30 // token有效期
)
const XRequestID = "X-Request-ID"

func GetTxApiSecret() (string, string) {
	return TxCloudApiSecretId, TxCloudApiSecretKey
}

func SetToken(ctx *gin.Context, token string) {
	ctx.Set(CustomTokenKey, token)
}

func GetToken(ctx *gin.Context) (token string) {
	if token, ok := ctx.Get(CustomTokenKey); ok {
		return com.ToString(token)
	}

	return ""
}
func MakeRequestID() string {
	return fmt.Sprintf("%d", uuid.New().ID())
}

func NewContextWithRequestID(params ...string) context.Context {
	if len(params) > 0 && params[0] != "" {
		return context.WithValue(context.Background(), XRequestID, params[0])
	} else {
		return context.WithValue(context.Background(), XRequestID, MakeRequestID())
	}
}

func GetRequestID(ctx context.Context) string {
	if ctx != nil && ctx.Value(XRequestID) != nil {
		if val := ctx.Value(XRequestID).(string); val != "" {
			return val
		}
	}

	return ""
}

func SetUserInfo(ctx *gin.Context, claims *CustomClaims) {
	ctx.Set(CustomClaimsKey, claims)
}

func PrintUserInfoDebug(c *gin.Context, claims *CustomClaims) {
	if flagDebug, ok := c.GetQuery("debug"); ok && flagDebug == "x" {
		if claims == nil {
			claims = GetUserInfo(c)
		}

		c.Header("user_id", com.ToString(claims.UserId))
		c.Header("operator_id", com.ToString(claims.OperatorId))
	}
}

func GenerateTokenUserId(userId int64) (string, error) {
	jwtClient := NewJwt()
	claims := CustomClaims{
		UserId:     userId,
		OperatorId: userId,

		StandardClaims: jwt.StandardClaims{
			NotBefore: int64(time.Now().Unix() - 1000), // 签名生效时间
			//ExpiresAt: int64(time.Now().Unix() + 10),   // 过期时间 一小时
			ExpiresAt: int64(time.Now().Unix() + TokenExpiredSeconds), // 过期时间 一个月

			Issuer: "x-web-api", // 签名的发行者
		},
	}

	token, err := jwtClient.CreateToken(claims)
	return token, err
}

func GenerateTokenDeviceId(deviceId int64) (string, error) {
	jwtClient := NewJwt()
	claims := CustomClaims{
		UserId:       deviceId,
		OperatorId:   deviceId,
		MainRoleList: []int64{RoleIdDevice},

		StandardClaims: jwt.StandardClaims{
			NotBefore: int64(time.Now().Unix() - 1000), // 签名生效时间
			//ExpiresAt: int64(time.Now().Unix() + 10),   // 过期时间 一小时
			ExpiresAt: int64(time.Now().Unix() + TokenExpiredSeconds), // 过期时间 一个月

			Issuer: "x-web-api", // 签名的发行者
		},
	}

	token, err := jwtClient.CreateToken(claims)
	return token, err
}

func GetUserInfo(ctx *gin.Context, authServiceUrls ...string) (claims *CustomClaims) {
	if claim, ok := ctx.Get(CustomClaimsKey); ok {
		claims = claim.(*CustomClaims)

		if claims.UserId == 0 || claims.UserId == claims.OperatorId {
			// authServiceUrl := ""
			if len(authServiceUrls) > 0 {
				//		// 使用服务地址调用
				//		authServiceUrl = authServiceUrls[0]
				//		service := apiclient.NewAuthService(GetToken(ctx), authServiceUrl, nil)
				//		if paUserId, _, err := service.PaUserId(claims.UserId); err != nil {
				//			log.Error(err)
				//		} else {
				//			claims.UserId = paUserId
				//		}
			} else {
			}
		}
		if claims.UserId == 0 {
			claims.UserId = claims.OperatorId
		}

		return claims
	}

	return nil
}

// 获取主账号ID和主账号角色列表
func GetUserInfoV2(ctx *gin.Context, authServiceUrls ...string) (claims *CustomClaims) {
	if claim, ok := ctx.Get(CustomClaimsKey); ok {
		claims = claim.(*CustomClaims)

		if claims.UserId == 0 || claims.UserId == claims.OperatorId {
			// authServiceUrl := "http://localhost:8501"
			//authServiceUrl := "http://auth-dev.x.jiutiandata.com"
			if len(authServiceUrls) > 0 && authServiceUrls[0] != "" {
				// 使用服务地址调用
				// authServiceUrl = authServiceUrls[0]
			} else {
				if com.IsLocal() {
					// authServiceUrl = "http://auth.dev.iot.bidevice.com"
				}
			}

			if len(claims.MainRoleList) == 0 { // 如果解密之后已经有角色说明在登陆时就已经携带了角色信息
				//service := apiclient.NewAuthService(GetToken(ctx), authServiceUrl, nil)
				//if paUserId, roleIdList, err := service.GetPaUserRoleIdList(ctx.GetHeader("Referer")); err != nil {
				//	log.Error(err)
				//} else {
				//	claims.UserId = paUserId
				//	claims.MainRoleList = roleIdList
				//}
			}
		}
		if claims.UserId == 0 {
			claims.UserId = claims.OperatorId
		}

		return claims
	}

	return nil
}

func GetAuthInfo(ctx *gin.Context) (claims *CustomClaims) {
	if claim, ok := ctx.Get(CustomClaimsKey); ok && claim != nil {
		claims = claim.(*CustomClaims)
		if claims.OperatorId != 0 {
			var err error
			if claims.UserId, claims.MainRoleList, err = table.GetLoginInfo(claims.OperatorId); err != nil {
				logger.Error(err)
			}
			if claims.UserId == 0 {
				claims.UserId = claims.OperatorId
			}
		}

		return claims
	}

	return &CustomClaims{}
}

// 设置token, 适用于不登录也可以访问的接口
func JwtSetToken(signKeys ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		var token string

		if token == "" {
			header := c.Request.Header.Get("Authorization")

			if len(header) == 0 {
				return
			}
			_, _ = fmt.Sscanf(header, "Bearer %s", &token)
		}

		if len(token) == 0 {
			return
		}

		j := NewJwt(signKeys...)
		// parseToken 解析token包含的信息
		claims, err := j.ParseToken(token)
		if err != nil {
			if err == TokenExpired {
				api.No(c, errno.ErrTokenExpired)
			} else {
				api.No(c, errno.ErrTokenInvalid)
			}

			c.Abort()
			return
		}

		PrintUserInfoDebug(c, claims)

		// 设置用户上下文
		SetUserInfo(c, claims)
		// 设置token
		SetToken(c, token)

	}
}

// JwtAuth 中间件，检查token
func JwtAuth(signKeys ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		var token string

		// from rpc auto set token
		if rpcToken, ok := c.GetQuery(RpcTokenKey); ok && rpcToken == RpcTokenPassword {
			if s, e := GenerateTokenUserId(AdminIdKey); e != nil {
				api.No(c, errno.ErrRpcPwdNotMatch)
				c.Abort()
				return
			} else {
				token = s
			}
		}

		// 兼容query中传参数
		if _, ok := c.GetQuery("token"); ok {
			token, _ = c.GetQuery("token")
		}

		if token == "" {
			header := c.Request.Header.Get("Authorization")

			if len(header) == 0 {
				api.No(c, errno.ErrMissingHeader)
				c.Abort()
				return
			}
			_, _ = fmt.Sscanf(header, "Bearer %s", &token)
		}

		if len(token) == 0 {
			api.No(c, errno.ErrAuthMissBearer)
			c.Abort()
			return
		}

		j := NewJwt(signKeys...)
		// parseToken 解析token包含的信息
		claims, err := j.ParseToken(token)
		if err != nil {
			if err == TokenExpired {
				api.No(c, errno.ErrTokenExpired)
			} else {
				api.No(c, errno.ErrTokenInvalid)
			}

			c.Abort()
			return
		}

		PrintUserInfoDebug(c, claims)

		// 设置用户上下文
		SetUserInfo(c, claims)
		// 设置token
		SetToken(c, token)
	}
}

// XJwt 签名结构
type XJwt struct {
	SigningKey []byte
}

// 一些常量
var (
	TokenExpired     error  = errors.New("Token is expired")
	TokenNotValidYet error  = errors.New("Token not active yet")
	TokenMalformed   error  = errors.New("That's not even a token")
	TokenInvalid     error  = errors.New("Couldn't handle this token:")
	SignKey          string = "1@&8@@a_04&k2Dk#3k23lh%^r7^^2=d%)o#)$z%3mznh4(3)$g&w"
)

// 载荷，可以加一些自己需要的信息
type CustomClaims struct {
	UserId       int64   `json:"userId"`
	OperatorId   int64   `json:"operatorId"`
	MerchantId   int64   `json:"merchantId"`
	MainRoleList []int64 `json:"mainRoleList"` // 主账号角色

	jwt.StandardClaims
}

func GetClaims(c *gin.Context, signKeys ...string) (token string, claims *CustomClaims, err error) {
	if token == "" {
		header := c.Request.Header.Get("Authorization")

		if len(header) == 0 {
			return
		}
		_, _ = fmt.Sscanf(header, "Bearer %s", &token)
	}

	if len(token) == 0 {
		return
	}

	j := NewJwt(signKeys...)
	// parseToken 解析token包含的信息
	claims, err = j.ParseToken(token)

	return
}

// 判断当前角色是否是设备端
func (c *CustomClaims) IsDevice() bool {
	return (len(c.MainRoleList) == 1 && c.MainRoleList[0] == RoleIdDevice)
}

// 新建一个jwt实例
func NewJwt(signKeys ...string) *XJwt {
	signKey := GetSignKey()
	if len(signKeys) > 0 {
		signKey = signKeys[0]
	}

	return &XJwt{
		[]byte(signKey),
	}
}

// 获取signKey
func GetSignKey() string {
	return SignKey
}

// 这是SignKey
func SetSignKey(key string) string {
	SignKey = key
	return SignKey
}

// CreateToken 生成一个token
func (j *XJwt) CreateToken(claims CustomClaims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(j.SigningKey)
}

// 解析Tokne
func (j *XJwt) ParseToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, TokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, TokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, TokenNotValidYet
			} else {
				return nil, TokenInvalid
			}
		}
	}
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, TokenInvalid
}

// 更新token
func (j *XJwt) RefreshToken(tokenString string) (string, error) {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	if err != nil {
		return "", err
	}
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
		return j.CreateToken(*claims)
	}
	return "", TokenInvalid
}

func NewState(data string) (*State, error) {
	decode, err := com.JwtDecode(data, "")
	return &State{MapClaims: decode}, err
}

type State struct {
	jwt.MapClaims
}

func (this *State) ToString() string {
	if encode, err := com.JwtEncode(this.MapClaims, ""); err != nil {
		return ""
	} else {
		return encode
	}
}

func (this *State) Put(key string, val interface{}) {
	this.MapClaims[key] = val
}

func (this *State) Get(key string) interface{} {
	return this.MapClaims[key]
}

func (this *State) Contains(key string) bool {
	var ok bool
	_, ok = this.MapClaims[key]
	return ok
}

func (this *State) IsEmpty() bool {
	return len(this.MapClaims) == 0
}

func (this *State) Clear() {
	this.MapClaims = make(jwt.MapClaims)
}
